package k8s

import (
	"context"
	"time"

	dfv1 "dataflow-operator/api/v1"

	logr "github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/client-go/discovery"
	"k8s.io/kubectl/pkg/cmd/top"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	metricsapi "k8s.io/metrics/pkg/apis/metrics"
	metricsv1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
	metricsclientset "k8s.io/metrics/pkg/client/clientset/versioned"
)

var (
	MeasuredResources = []corev1.ResourceName{
		corev1.ResourceCPU,
		corev1.ResourceMemory,
	}
)

func GetKubectlFactory() cmdutil.Factory {
	kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag()
	matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
	f := cmdutil.NewFactory(matchVersionKubeConfigFlags)
	return f
}

func MetricsAPIAvailable() bool {
	res := false
	var discoveryClient discovery.DiscoveryInterface
	apiGroups, err := discoveryClient.ServerGroups()
	if err != nil {
		logr.Error(err)
	} else {
		res = top.SupportedMetricsAPIVersionAvailable(apiGroups)
	}

	return res
}

func CalculatePodMetrics(podList *corev1.PodList, podMetricsList *metricsapi.PodMetricsList) (*dfv1.DataflowAppList, error) {
	podMetricsMap := make(map[string]metricsapi.PodMetrics)
	for _, podMetrics := range podMetricsList.Items {
		podMetricsMap[podMetrics.Name] = podMetrics
	}

	var appPods dfv1.DataflowAppList
	for _, pod := range podList.Items {
		name := pod.Name
		appPod := dfv1.DataflowApp{
			Name:      name,
			Namespace: pod.Namespace,
			Status: dfv1.DataflowAppStatus{
				NodeName:  pod.Spec.NodeName,
				HostIP:    pod.Status.HostIP,
				PodIP:     pod.Status.PodIP,
				StartTime: pod.Status.StartTime.UnixNano() / 1e6,
				PodPhase:  string(pod.Status.Phase),
			},
		}
		if pm, found := podMetricsMap[name]; !found {
			appPod.Status.Time = time.Now().UnixNano() / 1e6
		} else {
			appPod.Status.Time = pm.Timestamp.Time.UnixNano() / 1e6
			for _, c := range pm.Containers {
				cpu := c.Usage[MeasuredResources[0]]
				appPod.Status.CPU = float64(cpu.MilliValue()) / 1000.0
				memory := c.Usage[MeasuredResources[1]]
				appPod.Status.Memory = float64(memory.MilliValue()) / 1000.0
			}
		}
		appPods.Items = append(appPods.Items, appPod)
	}
	return &appPods, nil
}

func GetMetricsFromMetricsAPI(metricsClient metricsclientset.Interface, namespace,
	resourceName string, allNamespaces bool, selector labels.Selector) (*metricsapi.PodMetricsList, error) {
	var err error
	ns := metav1.NamespaceAll
	if !allNamespaces {
		ns = namespace
	}
	versionedMetrics := &metricsv1beta1api.PodMetricsList{}
	if resourceName != "" {
		m, err := metricsClient.MetricsV1beta1().PodMetricses(ns).Get(context.TODO(), resourceName, metav1.GetOptions{})
		if err != nil {
			return nil, err
		}
		versionedMetrics.Items = []metricsv1beta1api.PodMetrics{*m}
	} else {
		versionedMetrics, err = metricsClient.MetricsV1beta1().PodMetricses(ns).List(context.TODO(),
			metav1.ListOptions{LabelSelector: selector.String()})
		if err != nil {
			return nil, err
		}
	}
	metrics := &metricsapi.PodMetricsList{}
	err = metricsv1beta1api.Convert_v1beta1_PodMetricsList_To_metrics_PodMetricsList(versionedMetrics, metrics, nil)
	if err != nil {
		return nil, err
	}
	return metrics, nil
}
